﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.CampaignSystem.MapEvents.RaidEventComponent
// Assembly: TaleWorlds.CampaignSystem, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: E85F8C15-4DF6-4E9C-A58A-29177E40D07A
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\bin\Win64_Shipping_Client\TaleWorlds.CampaignSystem.dll

using System.Collections.Generic;
using System.Linq;
using TaleWorlds.CampaignSystem.Actions;
using TaleWorlds.CampaignSystem.CharacterDevelopment;
using TaleWorlds.CampaignSystem.Encounters;
using TaleWorlds.CampaignSystem.Party;
using TaleWorlds.CampaignSystem.Roster;
using TaleWorlds.CampaignSystem.Settlements;
using TaleWorlds.Core;
using TaleWorlds.Library;
using TaleWorlds.Localization;
using TaleWorlds.SaveSystem;

#nullable disable
namespace TaleWorlds.CampaignSystem.MapEvents
{
  public class RaidEventComponent : MapEventComponent
  {
    [SaveableField(10)]
    private float _nextSettlementDamage;
    [SaveableField(30)]
    private int _lootedItemCount;
    [SaveableField(40)]
    private Dictionary<ItemObject, float> _raidProductionRewards = new Dictionary<ItemObject, float>();

    internal static void AutoGeneratedStaticCollectObjectsRaidEventComponent(
      object o,
      List<object> collectedObjects)
    {
      ((MapEventComponent) o).AutoGeneratedInstanceCollectObjects(collectedObjects);
    }

    protected override void AutoGeneratedInstanceCollectObjects(List<object> collectedObjects)
    {
      base.AutoGeneratedInstanceCollectObjects(collectedObjects);
      collectedObjects.Add((object) this._raidProductionRewards);
    }

    internal static object AutoGeneratedGetMemberValueRaidDamage(object o)
    {
      return (object) ((RaidEventComponent) o).RaidDamage;
    }

    internal static object AutoGeneratedGetMemberValue_nextSettlementDamage(object o)
    {
      return (object) ((RaidEventComponent) o)._nextSettlementDamage;
    }

    internal static object AutoGeneratedGetMemberValue_lootedItemCount(object o)
    {
      return (object) ((RaidEventComponent) o)._lootedItemCount;
    }

    internal static object AutoGeneratedGetMemberValue_raidProductionRewards(object o)
    {
      return (object) ((RaidEventComponent) o)._raidProductionRewards;
    }

    [SaveableProperty(20)]
    public float RaidDamage { get; private set; }

    public MapEventSide DefenderSide => this.MapEvent.DefenderSide;

    public MapEventSide AttackerSide => this.MapEvent.AttackerSide;

    public Settlement MapEventSettlement => this.MapEvent.MapEventSettlement;

    public bool IsPlayerMapEvent => this.MapEvent.IsPlayerMapEvent;

    public BattleState BattleState => this.MapEvent.BattleState;

    protected RaidEventComponent(MapEvent mapEvent)
      : base(mapEvent)
    {
    }

    public static RaidEventComponent CreateRaidEvent(
      PartyBase attackerParty,
      PartyBase defenderParty)
    {
      MapEvent mapEvent = new MapEvent();
      RaidEventComponent component = new RaidEventComponent(mapEvent);
      mapEvent.Initialize(attackerParty, defenderParty, (MapEventComponent) component, MapEvent.BattleTypes.Raid);
      if (defenderParty.Settlement?.MilitiaPartyComponent != null)
        defenderParty.Settlement.MilitiaPartyComponent.MobileParty.MapEventSide = mapEvent.DefenderSide;
      Campaign.Current.MapEventManager.OnMapEventCreated(mapEvent);
      return component;
    }

    public static RaidEventComponent CreateComponentForOldSaves(
      MapEvent mapEvent,
      float nextSettlementDamage,
      int lootedItemCount,
      float raidDamage)
    {
      return new RaidEventComponent(mapEvent)
      {
        _nextSettlementDamage = nextSettlementDamage,
        _lootedItemCount = lootedItemCount,
        RaidDamage = raidDamage
      };
    }

    protected override void OnInitialize()
    {
      this._nextSettlementDamage = 0.0f;
      this.RaidDamage = 0.0f;
      ChangeVillageStateAction.ApplyBySettingToBeingRaided(this.MapEventSettlement, this.AttackerSide.LeaderParty.MobileParty);
      if (this._raidProductionRewards == null)
        this._raidProductionRewards = new Dictionary<ItemObject, float>();
      else
        this._raidProductionRewards.Clear();
    }

    protected override void OnFinalize()
    {
      if (this.DefenderSide.Parties.Count <= 1)
        CampaignEventDispatcher.Instance.RaidCompleted(this.BattleState == BattleState.AttackerVictory ? BattleSideEnum.Attacker : BattleSideEnum.Defender, this);
      if ((double) this.MapEventSettlement.SettlementHitPoints > 0.0)
        ChangeVillageStateAction.ApplyBySettingToNormal(this.MapEventSettlement);
      if (this._raidProductionRewards == null)
        return;
      this._raidProductionRewards.Clear();
    }

    internal override void Finish()
    {
      float? settlementHitPoints = this.MapEventSettlement?.SettlementHitPoints;
      double? nullable = settlementHitPoints.HasValue ? new double?((double) settlementHitPoints.GetValueOrDefault()) : new double?();
      double num = 1E-05;
      if (nullable.GetValueOrDefault() <= num & nullable.HasValue)
        ChangeVillageStateAction.ApplyBySettingToLooted(this.MapEventSettlement, this.AttackerSide.LeaderParty.MobileParty);
      if (!this.IsPlayerMapEvent || PlayerEncounter.Current == null)
        return;
      PlayerEncounter.Current.FinishRaid();
    }

    internal override void Update(ref bool finish)
    {
      if (this.DefenderSide.TroopCount == 0)
      {
        this._nextSettlementDamage += Campaign.Current.Models.RaidModel.CalculateHitDamage(this.AttackerSide, this.MapEventSettlement.SettlementHitPoints);
        if ((double) this._nextSettlementDamage <= 0.05)
          return;
        float num1 = -1f;
        if (this.MapEventSettlement.IsVillage)
        {
          num1 = this._nextSettlementDamage * 0.5f * this.MapEventSettlement.Village.Hearth;
          this.MapEventSettlement.Village.Hearth -= num1;
          Hero leaderHero = this.AttackerSide.LeaderParty.LeaderHero;
          if (leaderHero != null)
          {
            int num2 = (int) ((double) num1 * (double) Campaign.Current.Models.RaidModel.GoldRewardForEachLostHearth);
            if (num2 > 0)
            {
              if (leaderHero == Hero.MainHero)
              {
                TextObject text = GameTexts.FindText("str_plunder_gain_message");
                text.SetTextVariable("GOLD", num2);
                MBInformationManager.AddQuickInformation(text);
              }
              GiveGoldAction.ApplyBetweenCharacters((Hero) null, leaderHero, num2, true);
            }
          }
        }
        this.MapEventSettlement.SettlementHitPoints -= this._nextSettlementDamage;
        this.RaidDamage += this._nextSettlementDamage;
        this.RaidDamage = MathF.Min(this.RaidDamage, 1f);
        int num3 = 0;
        foreach (MapEventParty party in (List<MapEventParty>) this.AttackerSide.Parties)
          num3 += party.Party.MemberRoster.TotalManCount;
        if (num3 > 0)
        {
          float settlementDamage = this._nextSettlementDamage;
          int num4 = 0;
          foreach (ItemRosterElement itemRosterElement in this.MapEventSettlement.ItemRoster)
            num4 += itemRosterElement.Amount;
          ItemRoster lootedItems = new ItemRoster();
          PartyBase partyBase = this.AttackerSide.LeaderParty;
          if (num4 > 0)
          {
            int num5 = MBRandom.RoundRandomized((float) num4 * settlementDamage);
            if (num5 > num4)
              num5 = num4;
            EquipmentElement rosterElement = this.MapEventSettlement.ItemRoster.First<ItemRosterElement>().EquipmentElement;
            for (int index = 0; index < num5; ++index)
            {
              int num6 = (int) ((double) MBRandom.RandomFloat * (double) num4);
              bool flag = false;
              foreach (ItemRosterElement itemRosterElement in this.MapEventSettlement.ItemRoster)
              {
                int amount = itemRosterElement.Amount;
                num6 -= amount;
                if (num6 < 0)
                {
                  rosterElement = itemRosterElement.EquipmentElement;
                  int num7 = (int) ((double) MBRandom.RandomFloat * (double) num3);
                  foreach (MapEventParty party in (List<MapEventParty>) this.AttackerSide.Parties)
                  {
                    num7 -= party.Party.MemberRoster.TotalManCount;
                    if (num7 < 0)
                    {
                      partyBase = party.Party;
                      flag = true;
                      break;
                    }
                  }
                }
                if (flag)
                  break;
              }
              if (!rosterElement.IsEqualTo(EquipmentElement.Invalid))
              {
                this.MapEventSettlement.ItemRoster.AddToCounts(rosterElement, -1);
                if (this._lootedItemCount % 2 == 1)
                {
                  int number = 1;
                  if (rosterElement.Item.IsFood)
                  {
                    Hero leaderHero = partyBase.LeaderHero;
                    if ((leaderHero != null ? (leaderHero.GetPerkValue(DefaultPerks.Steward.EfficientCampaigner) ? 1 : 0) : 0) != 0)
                      ++number;
                  }
                  partyBase.ItemRoster.AddToCounts(rosterElement, number);
                  lootedItems.AddToCounts(rosterElement, number);
                }
                ++this._lootedItemCount;
                --num4;
                rosterElement = EquipmentElement.Invalid;
              }
            }
          }
          foreach ((ItemObject, float) production in (List<(ItemObject, float)>) this.MapEventSettlement.Village.VillageType.Productions)
          {
            if (this.MapEventSettlement.Village.VillageType.PrimaryProduction == DefaultItems.Grain && production.Item1 == DefaultItems.Grain || production.Item1 != DefaultItems.Grain)
            {
              float num8 = production.Item2;
              float num9 = (float) ((double) num1 * (double) num8 / 60.0);
              float num10;
              if (this._raidProductionRewards.TryGetValue(production.Item1, out num10))
                this._raidProductionRewards[production.Item1] = num10 + num9;
              else
                this._raidProductionRewards.Add(production.Item1, num9);
              if ((double) this._raidProductionRewards[production.Item1] >= 1.0)
              {
                int productionReward = (int) this._raidProductionRewards[production.Item1];
                this._raidProductionRewards[production.Item1] = this._raidProductionRewards[production.Item1] - (float) productionReward;
                this.LootItemInRaid(this.AttackerSide.LeaderParty, production.Item1, productionReward, ref lootedItems);
              }
            }
          }
          for (int index = 0; index < (int) num1; ++index)
          {
            if ((double) MBRandom.RandomFloat < 0.25)
              this.LootItemInRaid(this.AttackerSide.LeaderParty, MBRandom.ChooseWeighted<ItemObject>((IReadOnlyList<(ItemObject, float)>) Campaign.Current.Models.RaidModel.GetCommonLootItemScores()), 1, ref lootedItems);
          }
          if (lootedItems.Any<ItemRosterElement>() && partyBase?.MobileParty != null)
            CampaignEventDispatcher.Instance.OnItemsLooted(partyBase.MobileParty, lootedItems);
        }
        if ((double) this.MapEventSettlement.SettlementHitPoints <= 9.9999997473787516E-06)
        {
          this.MapEventSettlement.SettlementHitPoints = 0.0f;
          this.MapEvent.BattleState = BattleState.AttackerVictory;
          finish = true;
          this.MapEventSettlement.Party.SetLevelMaskIsDirty();
        }
        this._nextSettlementDamage = 0.0f;
      }
      else
      {
        if (this.AttackerSide.TroopCount != 0)
          return;
        finish = true;
      }
    }

    internal override void OnAfterLoad()
    {
      if (this._raidProductionRewards != null)
        return;
      this._raidProductionRewards = new Dictionary<ItemObject, float>();
    }

    private void LootItemInRaid(
      PartyBase party,
      ItemObject item,
      int count,
      ref ItemRoster lootedItems)
    {
      if (item.IsFood)
      {
        MobileParty mobileParty = party.MobileParty;
        if ((mobileParty != null ? (mobileParty.HasPerk(DefaultPerks.Steward.EfficientCampaigner) ? 1 : 0) : 0) != 0)
          ++count;
      }
      lootedItems.AddToCounts(item, count);
      party.ItemRoster.AddToCounts(item, count);
    }
  }
}
